package birdsong.filter;

import java.util.HashMap;
import java.util.Map;

import birdsong.comm.ServerComm;
import birdsong.comm.ServerSideMessage;
import birdsong.comm.ServerSideMessage.Type;
import birdsong.dummy.ServerSideMessageDummy;

public class SpamFilter extends ServerCommDecorator {

    // timeout: spam alinea b) (milisegundos)
    protected final static double TIMEOUT_TOEQUAL_MESSAGES = 30000;
    // number of messages required to send warning
    protected final static int MAX_EQUAL_MESSAGE_TOWARNING = 2;
    // number of messages required to disconnect user
    protected final static int MAX_EQUAL_MESSAGE_TODISCONNET = 3;

    protected final static double TIMEOUT_TOEXCESS_MESSAGES = 30000;
    protected final static int MAX_EXCESS_MESSAGE_TOWARNING = 8;
    protected final static int MAX_EXCESS_MESSAGE_TODISCONNECT = 10;

    protected final static double TIMEOUT_TOALLOW_CLIENT_DISCONNECTED = 30000;

    protected Map<String, MonitorEqualsMessage> _listMessageEquals;
    protected Map<String, MonitorExcessMessage> _listMessageExcess;
    protected Map<String, Message> _disconnectedClients;

    public SpamFilter(ServerComm concreteServer) {
	super(concreteServer);
	this._listMessageEquals = new HashMap<String, MonitorEqualsMessage>();
	this._listMessageExcess = new HashMap<String, MonitorExcessMessage>();
	this._disconnectedClients = new HashMap<String, Message>();
    }

    /**
     * @return ServerSideMessage
     */
    @Override
    public ServerSideMessage getNextMessage() {
	ServerSideMessage message = super.getNextMessage();
	String nickname = message.getClientNickname();
	String payload = message.getPayload();

	if (_disconnectedClients.containsKey(nickname)) {
	    if (Monitor.inTimeout(_disconnectedClients.get(nickname), TIMEOUT_TOALLOW_CLIENT_DISCONNECTED)) {
		// return this.getNextMessage();
		sendError(nickname, "Ainda nao passaram " + TIMEOUT_TOALLOW_CLIENT_DISCONNECTED / 1000 + " segundos");
		disconnectClient(nickname);
		ServerSideMessageDummy messageToDisconnectClient = new ServerSideMessageDummy(Type.CLIENT_DISCONNECTED, null, nickname);
		return messageToDisconnectClient;
	    } else {
		_disconnectedClients.remove(nickname);
	    }
	}

	if (message.getType() == Type.BIRDSONG_MESSAGE) {
	    if (_listMessageEquals.containsKey(nickname)) {
		_listMessageEquals.get(nickname).setMessage(payload);
	    } else {
		_listMessageEquals.put(nickname, new MonitorEqualsMessage(TIMEOUT_TOEQUAL_MESSAGES, MAX_EQUAL_MESSAGE_TODISCONNET,
			MAX_EQUAL_MESSAGE_TOWARNING));
		_listMessageEquals.get(nickname).setMessage(payload);
	    }

	    if (_listMessageExcess.containsKey(nickname)) {
		_listMessageExcess.get(nickname).setMessage(payload);
	    } else {
		_listMessageExcess.put(nickname, new MonitorExcessMessage(TIMEOUT_TOEXCESS_MESSAGES, MAX_EXCESS_MESSAGE_TODISCONNECT,
			MAX_EXCESS_MESSAGE_TOWARNING));
		_listMessageExcess.get(nickname).setMessage(payload);
	    }

	    if (_listMessageEquals.get(nickname).sendWarning()) {
		sendBirdsongMessage("TEST", nickname, "Warning: exceed the flow of messages with the same content");
	    }

	    if (_listMessageExcess.get(nickname).sendWarning()) {
		sendBirdsongMessage("TEST", nickname, "Warning: exceeded the number of messages allowed");
	    }

	    if (_listMessageEquals.get(nickname).disconnectClient() || _listMessageExcess.get(nickname).disconnectClient()) {
		ServerSideMessageDummy messageToDisconnectClient = new ServerSideMessageDummy(Type.CLIENT_DISCONNECTED, null, nickname);
		this.disconnectClient(nickname);
		return messageToDisconnectClient;
	    }
	}

	return message;
    }

    /**
     * @param nickname
     */
    @Override
    public void disconnectClient(String nickname) {
	_listMessageEquals.remove(nickname);
	_listMessageExcess.remove(nickname);
	if (!_disconnectedClients.containsKey(nickname)) {
	    _disconnectedClients.put(nickname, new Message(""));
	}
	super.disconnectClient(nickname);
    }

    /**
     * @param nickname
     * @return
     */
    @Override
    public boolean clientIsConnected(String nickname) {
	if (_disconnectedClients.containsKey(nickname))
	    if (Monitor.inTimeout(_disconnectedClients.get(nickname), TIMEOUT_TOALLOW_CLIENT_DISCONNECTED))
		return false;
	    else
		_disconnectedClients.remove(nickname);
	return super.clientIsConnected(nickname);
    }

}
